#include <cmath>
#include <windows.h>
#include <conio.h>
#include "bass.h"
#include "time.h"
#include <deque>
#include <iostream>
#pragma comment (lib, "bass.lib")

using namespace std;

#define E_major_number 0 /* 82.41  */
#define A_major_number 5 /* 110.00 */
#define D_minor_number 10/* 147.83 */
#define G_minor_number 15/* 196.00 */
#define B_minor_number 19/* 246.96 */
#define E_first_number 24/* 329.63 */

deque<int> samples;
double koef = 0.999;
int k = 2;
const int KEYS = 30;
const WORD keys[KEYS] = {
	'1','2','3','4','5','6','7','8','9','0',
	'Q','W','E','R','T','Y','U','I','O','P',
	'A','S','D','F','G','Z','X','C','V','B'};
int strings[150][44101];

class str
{
	int freq;
	int N;
	double koef;
	
public:
	str(int freq, int N, double koef);
	void pluck();
	double sample();
	deque<double> buf;
};

str::str(int freq, int SR, double koef)
{
	this -> freq = freq;
	this -> N = SR / freq;
	this -> koef = koef;
}

void str::pluck()
{
	double tmp;
	for (int i = 0; i < this -> N; i++)
	{
		tmp = (rand() + 17000);
		tmp /= 100000;
		if (rand() % 2)
			tmp *= -1;
		this -> buf.push_back(tmp);
	}
}

double str::sample()
{
	double val = this -> buf[0];
	double avg = this -> koef * 0.5 * (this -> buf[0] + this -> buf[1]);
	this -> buf.push_back(avg);
	this -> buf.pop_front();
	return avg;
}

void Error(const char *text) 
{
	printf("Error(%d): %s\n",BASS_ErrorGetCode(),text);
	BASS_Free();
	ExitProcess(0);
}

void run(double freq)
{
	str cur_str(freq, 44100, koef);
	cur_str.pluck();
	int sample = 0;
	for (int i = 0; i < 30000; ++i)
	{
		sample = int(cur_str.sample() * 34767);
		samples.push_back(sample);
	}
}

int main (void)
{
	cout << "wait, please\n";
	
	srand(time(NULL));

	INPUT_RECORD keyin;
	DWORD r;
	BASS_INFO info;

	BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 1);
	BASS_Init(-1, 44100, BASS_DEVICE_LATENCY, 0, NULL);
	BASS_GetInfo(&info);
	BASS_SetConfig(BASS_CONFIG_BUFFER, 200);

	int a[51] = {
		82.41, 87.31, 92.50, 98.00, 103.80, 110.00, 116.54, 123.48, 130.82, 138.59,
		147.83, 155.56, 164.81, 174.62, 185.00, 196.00, 207.00, 220.00, 233.08, 246.96, 261.63, 277.18, 
		293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88, 523.25, 
		554.36, 587.32, 622.26, 659.26, 698.46, 739.98, 784.00, 830.60, 880.00, 932.32, 987.75, 1046.50,
		1108.70, 1174.60, 1244.50, 1318.50, 1396.90, 1480.00
	};

	int position[6] = {E_first_number, B_minor_number, G_minor_number, D_minor_number, A_major_number, E_major_number};
	for (int i = 0; i < 48; ++i)
	{
		run(a[i]);
		for (int j = 0; !samples.empty() && j < 44100; ++j)
		{
			strings[i][j] = samples[0];
			samples.pop_front();
		}
		if (!samples.empty())
			samples.clear();
	}


	HSTREAM streams[6];
	for (int i = 0; i <= 5; ++i)
	{
		streams[i] = BASS_StreamCreate(44100, 1, 0, STREAMPROC_PUSH, 0);
		BASS_ChannelPlay(streams[i], FALSE);
	}

	cout << "ready for incoming";

	HFX fx[9]={0};
		
	DWORD SoundSpeed = 44100;
	int place = 0;
	while (ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &keyin, 1, &r)) 
	{
		int key;
		if (keyin.EventType != KEY_EVENT) 
			continue;
		if (keyin.Event.KeyEvent.wVirtualKeyCode == VK_SPACE) 
			break;
		if (keyin.Event.KeyEvent.wVirtualKeyCode == VK_OEM_PLUS) 
			SoundSpeed < 44100 ? SoundSpeed += 3000 : SoundSpeed;
		if (keyin.Event.KeyEvent.wVirtualKeyCode == VK_OEM_MINUS)
			SoundSpeed > 11100 ? SoundSpeed -= 3000 : SoundSpeed;
		if (keyin.Event.KeyEvent.wVirtualKeyCode == 'N')
			place == 0 ? place : --place;
		if (keyin.Event.KeyEvent.wVirtualKeyCode == 'M')
			place == 4 ? place : ++place;
		for (key = 0; key < KEYS; key++)
			if (keyin.Event.KeyEvent.wVirtualKeyCode == keys[key]) 
			{
				if (keyin.Event.KeyEvent.bKeyDown){ 
						BASS_StreamPutData(streams[key / 5], strings[position[key / 5] + key % 5 + place * 5], SoundSpeed);
				}

				break;
			}
	}

	BASS_Free();
	return 0;
}